package stardust.product.spider;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import stardust.product.spider.URLSource.URLEntity;


/**
 * 计算机爬虫抽象类。<br />
 * 用于定义爬虫程序的一些基本特性和参数。
 * <p />
 * @author ChenChang
 */
public abstract class WebcSpider
{
  
  public static final long SLEEP = 1000;
  
  /**
   * 抓取任务的URL源。
   */
  private URLSource source;
  
  /**
   * 抓取任务的最大线程数量。
   */
  private int threadCount;
  private ExecutorService executor;
  
  /**
   * 在当前任务中抓取的资源数量。
   */
  protected int fetchCount;
  
  /**
   * 在当前任务中上行网络消耗。
   */
  protected long fetchNetworkUploadSize;
  
  /**
   * 在当前任务中下行网络消耗。
   */
  protected long fetchNetworkDownloadSize;
  
  /**
   * 通知爬虫程序启动，激活后的爬虫程序抓取指定的URL地址上的资源内容。
   * <p />
   * @param url 抓取任务起始的站点地址。 
   * @param threadCount 在此爬虫程序实例中的最大可用线程数。
   */
  public void startup(URLSource source, int threadCount)
  {
    this.source = source;
    this.threadCount = threadCount;
    this.executor = Executors.newFixedThreadPool(this.threadCount);
    
    for (int i = 0; i < this.threadCount; i++)
    {
      this.executor.execute(new WebcSpider.WebcSpiderTaskFetcher());
    }
  }
  
  /**
   * 通知爬虫程序立即终止抓取，此方法将在所有抓取线程停止工作后返回。
   */
  public void shutdown()
  {
    this.source = null;
    
    this.executor.shutdown();
    this.executor = null;
  }
  
  /**
   * 爬虫程序抓取内容。
   * <p />
   * @param url 当前将要被爬取的URL地址。
   */
  public abstract void runTask(URLEntity urlEntity);
  
  private class WebcSpiderTaskFetcher implements Runnable
  {
    public void run()
    {
      URLSource source;
      URLEntity url;
      
      while (!Thread.interrupted() && ((source = WebcSpider.this.source) != null))
      {
        try
        {
          url = source.next();
          
          if (url == null)
          {
            Thread.sleep(WebcSpider.SLEEP);
          }
          else
          {
            WebcSpider.this.runTask(url);
          }
        }
        catch (InterruptedException e)
        {
          break;
        }
      }
    }
  }
  
}
